package com.sunseen.capacitormachine.commumication.serialport;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.util.SparseIntArray;

import androidx.annotation.Nullable;

import com.sunseen.capacitormachine.BuildConfig;
import com.sunseen.capacitormachine.common.MethodUtil;
import com.sunseen.capacitormachine.commumication.serialport.event.SurgeCurveEvent;
import com.sunseen.capacitormachine.commumication.serialport.event.SurgeDataEvent;
import com.sunseen.capacitormachine.commumication.serialport.event.SurgeParameterEvent;
import com.sunseen.capacitormachine.commumication.serialport.event.SurgeUidEvent;
import com.sunseen.capacitormachine.data.DataUtil;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ConcurrentLinkedQueue;

import android_serialport_api.SerialPort;

/**
 * 与浪涌测试模块通讯的服务
 *
 * @author zest
 */
public class SurgeService extends Service {
    private final static String TAG = SurgeService.class.getSimpleName();

    private static final String devicePath = "/dev/ttyS4";
    private static final int baudRate = 115200;

    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private Thread runThread;

    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.getDefault().register(this);
        runThread = new Thread(parseRun, "Surge");
        runThread.start();
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    private volatile boolean canRun = true;
    private Runnable parseRun = new Runnable() {
        @Override
        public void run() {
            try {
                mSerialPort = new SerialPort(new File(devicePath), baudRate, 0);
                mOutputStream = mSerialPort.getOutputStream();
                mInputStream = mSerialPort.getInputStream();
                int readSize;
                byte[] buffers = new byte[1024];
                while (canRun) {
                    try {
                        readSize = mInputStream.read(buffers);
                        if (readSize > 0) {
                            onDataReceive(buffers, readSize);
                        } else {
                            if (BuildConfig.DEBUG) {
                                Log.e(TAG, "read size = " + readSize);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "浪涌 串口读取线程异常");
                    }
                }
                Log.e(TAG, "浪涌 串口读取线程结束");
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "浪涌 串口开启发生异常");
            }
        }
    };

    private static final int frameHead = 0x55;
    private static final int frameEnd = 0xAA;
    private final int maxLen = 1008;
    private int[] datas = new int[maxLen];
    private int pos = 0;
    /**
     * headFlag = 0  -> 未检测到帧头
     * headFlag = 1  -> 检测到了1个帧头
     * headFlag = 2  -> 检测到了2个帧头
     * *
     */
    private int headFlag = 0;
    private boolean lastEndIsHead1 = false;
    private int head1Pos = -2;
    private boolean lastEndMayHead1 = false;
    /**
     * endFlag = 0  -> 未检测到帧尾
     * endFlag = 1  -> 检测到了1个帧尾
     * endFlag = 2  -> 检测到了2个帧尾
     * *
     */
    private int endFlag = 0;
    private boolean lastEndIsEnd1 = false;
    private int end1Pos = -2;
    private int curInt;

    /**
     * 接收到数据进行处理
     */
    private void onDataReceive(byte[] bytes, int size) {
//        Log.e(TAG, "onDataReceive:");
//        StringBuilder sb = new StringBuilder();
//        sb.append("                                     \r\n");
//        for (int i = 0; i <= pos; i++) {
//            if (i % 10 == 0) {
//                sb.append('\n');
//            }
//            sb.append(Integer.toHexString(bytes[i] & 0xFF));
//            sb.append(' ');
//        }
//        Log.e(TAG, sb.toString());

        for (int i = 0; i < size; i++) {
            curInt = bytes[i] & 0xFF;
            if (curInt == frameHead) {
                switch (headFlag) {
                    case 0: {
                        headFlag = 1;
                        head1Pos = i;
                        if (i == size - 1) {
                            lastEndIsHead1 = true;
                        }
                        datas[pos] = curInt;
                        pos++;
                    }
                    break;
                    case 1: {
                        if (lastEndIsHead1) {
                            if (i == 0) {
                                headFlag = 2;
                                datas[pos] = curInt;
                                pos++;
                            } else {
                                lastEndIsHead1 = false;
                                pos = 0;
                                datas[pos] = curInt;
                                pos++;
                            }
                        } else {
                            if (i - head1Pos == 1) {
                                headFlag = 2;
                                datas[pos] = curInt;
                                plusAndCheck();
                            } else {
                                head1Pos = i;
                                if (i == size - 1) {
                                    lastEndIsHead1 = true;
                                }
                                datas[pos] = curInt;
                                plusAndCheck();
                            }
                        }
                    }
                    break;
                    case 2: {
                        if (lastEndMayHead1) {
                            if (i == 0) {
                                datas[0] = frameHead;
                                datas[1] = frameHead;
                                pos = 2;
                            }
                            lastEndMayHead1 = false;
                        } else {
                            if (i + 1 < size) {
                                if ((bytes[i + 1] & 0xff) == frameHead) {
                                    //检测到了新的帧头
                                    //复位相关标志
                                    pos = 0;
                                    datas[pos] = frameHead;
                                    headFlag = 1;
                                    endFlag = 0;
                                    pos++;
                                } else {
                                    datas[pos] = curInt;
                                    plusAndCheck();
                                }
                            } else if (i == size - 1) {
                                lastEndMayHead1 = true;
                                datas[pos] = curInt;
                            }
                        }
                    }
                    break;
                    default:
                        Log.e(TAG, "headFlag 标识错误");
                        resetFlag();
                        break;
                }
            } else if (curInt == frameEnd) {
                if (headFlag == 2) {
                    //有帧头
                    if (endFlag == 0) {
                        endFlag = 1;
                        end1Pos = i;
                        if (i == size - 1) {
                            lastEndIsEnd1 = true;
                        }
                        datas[pos] = curInt;
                        plusAndCheck();
                    } else if (endFlag == 1) {
                        if (lastEndIsEnd1) {
                            if (i == 0) {
                                datas[pos] = curInt;
                                analysisFrame(datas, pos);
                                resetFlag();
                            } else {
                                end1Pos = i;
                                datas[pos] = curInt;
                                plusAndCheck();
                            }
                        } else {
                            if (i - end1Pos == 1) {
                                datas[pos] = curInt;
                                analysisFrame(datas, pos);
                                resetFlag();
                            } else {
                                end1Pos = i;
                                datas[pos] = curInt;
                                plusAndCheck();
                            }
                        }
                    }
                } else {
                    //数据错误
                    resetFlag();
                }
            } else {
                if (lastEndMayHead1) {
                    lastEndMayHead1 = false;
                }
                if (lastEndIsHead1) {
                    lastEndIsHead1 = false;
                }
                if (lastEndIsEnd1) {
                    lastEndIsEnd1 = false;
                }
                if (headFlag == 2) {
                    datas[pos] = curInt;
                    plusAndCheck();
                } else {
                    //数据错误
                    resetFlag();
                }
            }
        }
        Log.e(TAG, "size  = " + size);
    }

    private void resetFlag() {
        pos = 0;
        headFlag = 0;
        endFlag = 0;
    }


    private void plusAndCheck() {
        pos++;
        if (pos == maxLen) {
            Log.e(TAG, "数据出错，数据越界");
            resetFlag();
        }
    }

    /**
     * 0x55 0x55 01(01代表浪涌1，02代表浪涌2) 01(结果：02通过，01失败，00接触不良或空位)
     * 数据1(2字节) 数据2(2字节)... 数据n(2字节) 0xee 0xee(2字节校验) 0xAA 0xAA
     */
    SparseIntArray intArray = new SparseIntArray();

    private void analysisFrame(int[] frameData, int pos) {
        if (frameData.length < 9 || pos < 9) {
            Log.e(TAG, "帧数据长度错误");
            return;
        }
        //和校验 和校验除去帧头帧尾 浪涌标志字节 结果标志字节 校验位
        int sum = 0;
        for (int i = 4; i < pos - 3; i++) {
            sum += frameData[i];
        }
        sum = sum & 0xff;
        int checker = frameData[pos - 3];
        // 帧校验 第一位为0x01 表示 帧校验第二位需要加1
        if (frameData[pos - 2] == 1) {
            checker++;
        }
        Log.e(TAG, "sum = " + sum + " checker = " + checker);
        //根据和校验来处理数据，和校验失败，打Log不处理数据，和校验成功，解析处理数据
        if (sum == checker) {
            //校验成功
            intArray.clear();
            for (int i = 4; i <= pos - 4; i = i + 2) {
                intArray.append(i / 2 - 1, frameData[i] * 256 + frameData[i + 1]);
            }
            if (frameData[2] == 1) {
                if (!surge1UidQueue.isEmpty()) {
                    String surge1Uid = surge1UidQueue.poll();
                    if (surge1Uid != null) {
                        EventBus.getDefault().post(new SurgeDataEvent(surge1Uid,
                                DataUtil.producingParameter.getSurgeUpperLimitVoltage(),
                                frameData[2], frameData[3], intArray));
                    }
                }
            } else {
                if (!surge2UidQueue.isEmpty()) {
                    String surge2Uid = surge2UidQueue.poll();
                    if (surge2Uid != null) {
                        EventBus.getDefault().post(new SurgeDataEvent(surge2Uid,
                                DataUtil.producingParameter.getSurgeUpperLimitVoltage(),
                                frameData[2], frameData[3], intArray));
                    }
                }
            }
            EventBus.getDefault().post(new SurgeCurveEvent(frameData[2], frameData[3], intArray));
            Log.e(TAG, "和校验成功");
        } else {
            Log.e(TAG, "和校验失败");
        }
    }

    private ConcurrentLinkedQueue<String> surge1UidQueue = new ConcurrentLinkedQueue<>();
    private ConcurrentLinkedQueue<String> surge2UidQueue = new ConcurrentLinkedQueue<>();

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onSurgeUidEvent(SurgeUidEvent event) {
        if (event.getType() == 1) {
            surge1UidQueue.offer(event.getUid());
        } else if (event.getType() == 2) {
            surge2UidQueue.offer(event.getUid());
        }
    }

    private void sendData(byte[] bytes) {
        try {
            mOutputStream.write(bytes);
            mOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "sendData exception");
        }
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onSendSurgeUpperLimitEvent(SurgeParameterEvent event) {
        byte[] bytes = new byte[4];
        bytes[0] = 0x55;
        bytes[1] = 0x03;
        bytes[2] = (byte) (event.getSurgeUpperLimit() / 256);
        bytes[3] = (byte) (event.getSurgeUpperLimit() % 256);
        MethodUtil.logHexBytes(TAG, bytes, 0, 4, 10);
        sendData(bytes);
    }

    @Override
    public void onDestroy() {
        EventBus.getDefault().unregister(this);
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
        canRun = false;
        runThread.interrupt();
        super.onDestroy();
    }
}
